#pragma once
#include <iostream>
#include <memory>
#include <unordered_map>

#include "Connection.hpp"
#include "Epoller.hpp"

#define maxevents 64

using Connection_t = std::shared_ptr<Connection>;
class Reactor
{
public:
    Reactor()
        : _epoller(std::make_unique<epoller>()), _isrunning(false)
    {
    }

    void Loop()
    {
        _isrunning = true;
        int timeout = -1;
        while (_isrunning)
        {
            int n = _epoller->Wait(maxevents, _revents, timeout);
            switch (n)
            {
            case 0:
                LOG(LogLevel::INFO) << "time out...";
                break;
            case -1:
                LOG(LogLevel::FATAL) << "epoll_wait_error";
                break;
            default:
                LOG(LogLevel::INFO) << "epoll_wait_success";
                DisPatcher(n); // 进行事件派发
                break;
            }
        }
        _isrunning = false;
    }

    void DisPatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            if (_revents[i].events & EPOLLERR || _revents[i].events & EPOLLHUP)
            {
                _revents[i].events = EPOLLIN | EPOLLOUT;
            }
            if (IsInConnection(_revents[i].data.fd)) // 连接先要存在，因为如果发生异常会断开连接，此时也有可能有事件就绪，如果连接不存在不能进行派发
            {
                if (_revents[i].events & EPOLLIN)
                {
                    _Connections[_revents[i].data.fd]->Recver();
                }
                if (_revents[i].events & EPOLLOUT)
                {
                    _Connections[_revents[i].data.fd]->Sender();
                }
            }
        }
    }

    void InsertConnection(Connection_t coon)
    {
        auto pos = _Connections.find(coon->Sockfd());

        if (pos == _Connections.end())
        {
            _Connections.insert(std::make_pair(coon->Sockfd(), coon)); // 将模块插入Reactor中
                                                                       // 注意我这里已经把coon进行move了，所以不能coon已经是随机值了
                                                                       // 底下的Add不能够再利用coon了
            // 2. 把新插入进来的连接，写透到内核的epoll中
            _epoller->Add(coon->Sockfd(), coon->GetEvents());
            // 3. 设置关联关系，让connection回指当前对象
            coon->SetOwner(this);
            LOG(LogLevel::DEBUG) << "add connection success: " << coon->Sockfd();
        }
        DebugPrint(); // 每次插入新的Connection都debug一下
    }

    bool IsInConnection(int sockfd)
    {
        auto pos = _Connections.find(sockfd);
        return pos != _Connections.end(); // 存在返回true
    }

    void EnableCtrl(int sockfd, bool read, bool write)
    {
        if (IsInConnection(sockfd))
        {
            uint32_t reset_events = (read == true ? EPOLLIN : 0) | (write == true ? EPOLLOUT : 0) | EPOLLET;
            _Connections[sockfd]->SetEvents(reset_events);
        }
    }

    void DelConnection(int sockfd)
    {
        if (IsInConnection(sockfd))
        {
            // 1.移除关心
            _epoller->Delete(sockfd);
            // 2.关闭文件描述符
            ::close(_Connections[sockfd]->Sockfd());
            // 3.删除connection_t所指向的资源
            _Connections[sockfd].reset(); // 释放ptr所管理的资源，ptr 变为空
            // 4.从_Connections移除
            _Connections.erase(sockfd);
        }
    }

    void DebugPrint()
    {
        std::cout << "Reactor关心的fd有: ";
        for (auto &ch : _Connections)
        {
            std::cout << ch.first << " ";
        }
        std::cout << std::endl;
    }

    ~Reactor()
    {
    }

private:
    std::unordered_map<int, Connection_t> _Connections;
    std::unique_ptr<epoller> _epoller;
    struct epoll_event _revents[maxevents];
    bool _isrunning;
};